package owl2vowl.parser.vowl.property;

import java.util.Iterator;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.semanticweb.owlapi.model.IRI;
import org.semanticweb.owlapi.model.OWLAsymmetricObjectPropertyAxiom;
import org.semanticweb.owlapi.model.OWLClassExpression;
import org.semanticweb.owlapi.model.OWLDisjointObjectPropertiesAxiom;
import org.semanticweb.owlapi.model.OWLEquivalentObjectPropertiesAxiom;
import org.semanticweb.owlapi.model.OWLFunctionalObjectPropertyAxiom;
import org.semanticweb.owlapi.model.OWLInverseFunctionalObjectPropertyAxiom;
import org.semanticweb.owlapi.model.OWLInverseObjectPropertiesAxiom;
import org.semanticweb.owlapi.model.OWLIrreflexiveObjectPropertyAxiom;
import org.semanticweb.owlapi.model.OWLObjectProperty;
import org.semanticweb.owlapi.model.OWLObjectPropertyDomainAxiom;
import org.semanticweb.owlapi.model.OWLObjectPropertyExpression;
import org.semanticweb.owlapi.model.OWLObjectPropertyRangeAxiom;
import org.semanticweb.owlapi.model.OWLProperty;
import org.semanticweb.owlapi.model.OWLReflexiveObjectPropertyAxiom;
import org.semanticweb.owlapi.model.OWLSubObjectPropertyOfAxiom;
import org.semanticweb.owlapi.model.OWLSymmetricObjectPropertyAxiom;
import org.semanticweb.owlapi.model.OWLTransitiveObjectPropertyAxiom;

import owl2vowl.constants.VowlAttribute;
import owl2vowl.model.data.VowlData;
import owl2vowl.model.entities.nodes.AbstractNode;
import owl2vowl.model.entities.nodes.classes.NullClass;
import owl2vowl.model.entities.properties.AbstractProperty;
import owl2vowl.model.entities.properties.VowlObjectProperty;

public class ObjectPropertyVisitor extends PropertyVisitor {
	private Logger logger = LogManager.getLogger(ObjectPropertyVisitor.class);

	public ObjectPropertyVisitor(VowlData vowlData, OWLProperty owlObjectProperty) {
		super(vowlData, owlObjectProperty);
	}

	public void visit(OWLInverseFunctionalObjectPropertyAxiom axiom) {
		VowlObjectProperty prop = this.vowlData.getObjectPropertyForIri(this.owlObjectProperty.getIRI());
		prop.addAttribute(VowlAttribute.INVERSE_FUNCTIONAL);
	}

	public void visit(OWLFunctionalObjectPropertyAxiom axiom) {
		VowlObjectProperty prop = this.vowlData.getObjectPropertyForIri(this.owlObjectProperty.getIRI());
		prop.addAttribute(VowlAttribute.FUNCTIONAL);
	}

	public void visit(OWLIrreflexiveObjectPropertyAxiom axiom) {
		VowlObjectProperty prop = this.vowlData.getObjectPropertyForIri(this.owlObjectProperty.getIRI());
		prop.addAttribute(VowlAttribute.IRREFLEXIVE);
	}

	public void visit(OWLObjectPropertyDomainAxiom axiom) {
		VowlObjectProperty prop = this.vowlData.getObjectPropertyForIri(this.owlObjectProperty.getIRI());
		IRI domainIri;
		if (((OWLClassExpression) axiom.getDomain()).isAnonymous()) {
			AbstractNode anonymClass = (AbstractNode) ((OWLClassExpression) axiom.getDomain())
					.accept(new DomainRangeVisitor(this.owlObjectProperty, this.vowlData));
			if (anonymClass instanceof NullClass) {
				this.logger.info("Skipped anonymous object domain: " + axiom.getDomain());
				return;
			}

			domainIri = anonymClass.getIri();
		} else {
			if (((OWLClassExpression) axiom.getDomain()).asOWLClass().isOWLThing()) {
				return;
			}

			domainIri = ((OWLClassExpression) axiom.getDomain()).asOWLClass().getIRI();
		}

		prop.addDomain(domainIri);
		this.vowlData.getClassForIri(domainIri).addOutGoingProperty(prop.getIri());
	}

	public void visit(OWLObjectPropertyRangeAxiom axiom) {
		VowlObjectProperty prop = this.vowlData.getObjectPropertyForIri(this.owlObjectProperty.getIRI());
		IRI rangeIri;
		if (((OWLClassExpression) axiom.getRange()).isAnonymous()) {
			AbstractNode anonymClass = (AbstractNode) ((OWLClassExpression) axiom.getRange())
					.accept(new DomainRangeVisitor(this.owlObjectProperty, this.vowlData));
			if (anonymClass instanceof NullClass) {
				this.logger.info("Skipped anonymous object range: " + axiom.getRange());
				return;
			}

			rangeIri = anonymClass.getIri();
		} else {
			if (((OWLClassExpression) axiom.getRange()).asOWLClass().isOWLThing()) {
				return;
			}

			rangeIri = ((OWLClassExpression) axiom.getRange()).asOWLClass().getIRI();
		}

		prop.addRange(rangeIri);
		this.vowlData.getClassForIri(rangeIri).addInGoingProperty(prop.getIri());
	}

	public void visit(OWLDisjointObjectPropertiesAxiom axiom) {
		this.logger.info("Disjoint object properties not supported: " + axiom);
	}

	public void visit(OWLInverseObjectPropertiesAxiom axiom) {
		OWLObjectPropertyExpression firstProperty = axiom.getFirstProperty();
		OWLObjectPropertyExpression secondProperty = axiom.getSecondProperty();
		if (firstProperty.isAnonymous()) {
			this.logger.info("Anonym first property:" + firstProperty);
		} else if (secondProperty.isAnonymous()) {
			this.logger.info("Anonym second property:" + secondProperty);
		} else {
			OWLObjectProperty inverseProperty = firstProperty.asOWLObjectProperty();
			OWLObjectProperty baseProperty = secondProperty.asOWLObjectProperty();
			AbstractProperty inverseVowlProp = this.vowlData.getPropertyForIri(inverseProperty.getIRI());
			inverseVowlProp.addInverse(baseProperty.getIRI());
		}
	}

	public void visit(OWLSubObjectPropertyOfAxiom axiom) {
		OWLObjectPropertyExpression subProperty = (OWLObjectPropertyExpression) axiom.getSubProperty();
		OWLObjectPropertyExpression superProperty = (OWLObjectPropertyExpression) axiom.getSuperProperty();
		if (subProperty.isAnonymous()) {
			this.logger.info("Anonym sub property:" + subProperty);
		} else if (superProperty.isAnonymous()) {
			this.logger.info("Anonym super property:" + superProperty);
		} else {
			AbstractProperty subVowl = this.vowlData.getPropertyForIri(subProperty.asOWLObjectProperty().getIRI());
			AbstractProperty superVowl = this.vowlData.getPropertyForIri(superProperty.asOWLObjectProperty().getIRI());
			subVowl.addSuperEntity(superVowl.getIri());
			superVowl.addSubEntity(subVowl.getIri());
		}
	}

	public void visit(OWLEquivalentObjectPropertiesAxiom axiom) {
		AbstractProperty base = this.vowlData.getPropertyForIri(this.owlObjectProperty.getIRI());
		Iterator arg2 = axiom.getProperties().iterator();

		while (arg2.hasNext()) {
			OWLObjectPropertyExpression expr = (OWLObjectPropertyExpression) arg2.next();
			if (expr.isAnonymous()) {
				this.logger.info("Anonysmous equivalent prop: " + expr);
			} else if (!expr.asOWLObjectProperty().getIRI().equals(base.getIri())) {
				base.addEquivalentElement(expr.asOWLObjectProperty().getIRI());
			}
		}

	}

	public void visit(OWLAsymmetricObjectPropertyAxiom axiom) {
		VowlObjectProperty prop = this.vowlData.getObjectPropertyForIri(this.owlObjectProperty.getIRI());
		prop.addAttribute(VowlAttribute.ASYMMETRIC);
	}

	public void visit(OWLSymmetricObjectPropertyAxiom axiom) {
		VowlObjectProperty prop = this.vowlData.getObjectPropertyForIri(this.owlObjectProperty.getIRI());
		prop.addAttribute(VowlAttribute.SYMMETRIC);
	}

	public void visit(OWLReflexiveObjectPropertyAxiom axiom) {
		VowlObjectProperty prop = this.vowlData.getObjectPropertyForIri(this.owlObjectProperty.getIRI());
		prop.addAttribute(VowlAttribute.REFLEXIVE);
	}

	public void visit(OWLTransitiveObjectPropertyAxiom axiom) {
		VowlObjectProperty prop = this.vowlData.getObjectPropertyForIri(this.owlObjectProperty.getIRI());
		prop.addAttribute(VowlAttribute.TRANSITIVE);
	}
}